/* Copyright   1991 Gustavus Adolphus College.  All rights reserved.
 *
 * Schematik was developed by Gustavus Adolphus College (GAC) with
 * support from NeXT Computer, Inc.  Permission to copy this software,
 * to redistribute it, and to use it for any purpose is granted,
 * subject to the following restrictions and understandings.
 *
 * 1. Any copy made of this software must include this copyright
 * notice in full.
 *
 * 2. Users of this software agree to make their best efforts (a) to
 * return to the GAC Mathematics and Computer Science Department any
 * improvements or extensions that they make, so that these may be
 * included in future releases; and (b) to inform GAC of noteworthy
 * uses of this software.
 *
 * 3. All materials developed as a consequence of the use of this
 * software shall duly acknowledge such use, in accordance with the
 * usual standards of acknowledging credit in academic research.
 *
 * 4. GAC makes no express or implied warranty or representation of
 * any kind with respect to this software, including any warranty
 * that the operation of this software will be error-free.  ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
 * PURPOSE IS HEREBY DISCLAIMED.  GAC is under no obligation to
 * provide any services, by way of maintenance, update, or otherwise.
 *
 * 5. In conjunction with products arising from the use of this
 * material, there shall be no use of the name of Gustavus Adolphus
 * College nor of any adaptation thereof in any advertising,
 * promotional, or sales literature without prior written consent
 * from GAC in each case.
 */

#import "DocWin.h"
#import "../defines.h"
#import DocText_h
#import PrefAgent_h
#import Main_h
#import <appkit/OpenPanel.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <objc/List.h>
#import <streams/streams.h>
#import <sys/malloc.h>
#import <sys/stat.h>
#import <mach.h>
#import <NXCType.h>
#import <string.h>

extern char *stlchr(char *,int);

#define NIBFILE			"Doc.nib"
#define MINIWINDOWICON		"Doc"
#define DOCZONENAME		"DocWin"
#define MINWINDOWHEIGHT		67

static id findOpenFile(id, id, char *);

@implementation DocWin

+ new
{
    NXZone *zone=NXCreateZone(vm_page_size, vm_page_size, YES);

DEBUG_FUNC1(DEBUGLEVEL);
    DEBUG_ASSERT(zone!=NULL)
    self = LoadNIB(NIBFILE, self, zone); 
    DEBUG_ASSERT(self!=nil)
    docZone = zone;
    NXNameZone(docZone, DOCZONENAME);
    [self _newAuxiliary];
    [self setMiniwindowIcon:MINIWINDOWICON];
    DEBUG_ASSERT(([self miniwindowIcon]!=NULL)&&(*[self miniwindowIcon]))
    [self setTitleAsFilename:filename];
    DEBUG_ASSERT(([self title]!=NULL)&&(*[self title]))
    return self;
}

+ performOpenFromFile:(char *)aString
{
    NXStream *stream;
    BOOL freeString=NO;

DEBUG_FUNC1(DEBUGLEVEL);
    if (aString==NULL)
      {
        id openPanel=[OpenPanel new];
        char *types[]={DOCUMENTEXTENSION, DOCUMENTEXTENSION BACKUPEXTENSION, NULL};
	char *temp;

        DEBUG_ASSERT(openPanel!=nil)
        [openPanel allowMultipleFiles:NO];
        {id mainWin = [NXApp mainWindow];
         const char *fn;
         if(mainWin == nil || ![mainWin isKindOf:self])
           fn = [[NXApp interactionWindow] filename];
         else{
           fn = [mainWin filename];
         }
         [openPanel setDirectory:temp=stlchr(NXCopyStringBuffer(fn), PATHSEPARATOR)];
        }
	free(temp);
        if ([openPanel runModalForTypes:types]&&[openPanel filename]&&*[openPanel filename])
          {
            aString = NXCopyStringBuffer([openPanel filename]);
            DEBUG_ASSERT((aString!=NULL)&&(*aString))
            freeString = YES;
          }
        else
          return nil;
      }
    DEBUG_ASSERT((aString!=NULL)&&(*aString))
    if (self = findOpenFile(self, NXApp, aString))
      {
        [self makeKeyAndOrderFront:self];
        if (freeString) free(aString);
        return nil;
      }
    DEBUG_ASSERT(self==nil)
    if (stream = NXMapFile(aString, NX_READONLY))
      {
        self = [DocWin new];
        [self setFilename:aString];
        DEBUG_ASSERT((filename!=NULL)&&(*filename))
        [self setTitleAsFilename:filename];
        [textView readText:stream];
        NXCloseMemory(stream, NX_FREEBUFFER);
        [textView setSel:0 :0];
        named = YES;
        if (freeString) free(aString);
        return self;
      }
    NXRunAlertPanel(OPENALERTTITLE, FILEOPENERROR, NULL, NULL, NULL);
    if (freeString) free(aString);
    return nil;
}

- makeKeyAndOrderFront:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    [super makeKeyAndOrderFront:sender];
    [super makeFirstResponder:textView];
    [textView lockFocus];
    [textView showCaret];
    [textView unlockFocus];
    return self;
}

- free
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (filename)
      NXZoneFree(docZone, filename);
    return [super free];
}

- performFormat:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    [textView formatTextSelectionOnly:NO];
    return self;
}

- performRevert:sender
{
    NXStream *stream;

DEBUG_FUNC1(DEBUGLEVEL);
    if (named&&wFlags2.docEdited&&(NXRunAlertPanel(REVERTALERTTITLE, REVERTALERTQUERY, REVERTALERTDEFAULT, REVERTALERTALTERNATE, NULL, (strrchr(filename,'/')+1))==NX_ALERTDEFAULT))
      {
        if (stream = NXMapFile(filename, NX_READONLY))
          {
            [super disableFlushWindow];
            [textView readText:stream];
            NXCloseMemory(stream, NX_FREEBUFFER);
            [[[super display] reenableFlushWindow] flushWindow];
            [super setDocEdited:NO];
            [textView setSel:0 :0];
            return self;
          }
        NXRunAlertPanel(REVERTALERTTITLE, FILEOPENERROR, NULL, NULL, NULL);
      }
    return self;
}

- performSave:sender
{
    NXStream *stream;

DEBUG_FUNC1(DEBUGLEVEL);
    if (!named)
      return [self performSaveAs:self];
    if (stream = NXOpenMemory(NULL, 0, NX_WRITEONLY))
      {
        [self _saveAuxiliary:stream filename:filename];
        return self;
      }
    NXRunAlertPanel(SAVEALERTTITLE, FILESAVEERROR, NULL, NULL, NULL);
    return self;
}

- performSaveAs:sender
{
    NXStream *stream;
    id savePanel = [SavePanel new];
    char *temp;

DEBUG_FUNC1(DEBUGLEVEL);
    DEBUG_ASSERT(savePanel!=nil)
    [savePanel setRequiredFileType:DOCUMENTEXTENSION];
        {id mainWin = [NXApp mainWindow];
         const char *fn;
         if(mainWin == nil || ![mainWin isKindOf:self])
           fn = [[NXApp interactionWindow] filename];
         else{
           fn = [mainWin filename];
         }
    if ([savePanel runModalForDirectory:temp=stlchr(NXCopyStringBuffer(fn), PATHSEPARATOR) file:(strrchr(fn, PATHSEPARATOR)+1)]&&[savePanel filename]&&*[savePanel filename])
      {
        if (stream = NXOpenMemory(NULL, 0, NX_WRITEONLY))
          {
	    named=NO;
            [self _saveAuxiliary:stream filename:(char *)[savePanel filename]];
	    free(temp);
            return self;
          }
        NXRunAlertPanel(SAVEALERTTITLE, FILESAVEERROR, NULL, NULL, NULL);
      }
    }
    free(temp);
    return self;
}

- undo:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return self;
}

- print:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [[self textView] printPSCode:self];
}

- (id)scrollView
{
DEBUG_FUNC1(DEBUGLEVEL);
    return scrollView;
}

- (id)textView
{
DEBUG_FUNC1(DEBUGLEVEL);
    return textView;
}

- (const char *)filename
{
DEBUG_FUNC1(DEBUGLEVEL);
    return filename;
}

- setFilename:(const char *)aString
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (filename!=NULL)
      NXZoneFree(docZone, filename);
    filename = NXCopyStringBufferFromZone(aString, docZone);
    DEBUG_ASSERT(filename!=NULL)
    return self;
}

- (BOOL)named
{
DEBUG_FUNC1(DEBUGLEVEL);
    return named;
}

- setNamed:(BOOL)aBoolean
{
DEBUG_FUNC1(DEBUGLEVEL);
    named = aBoolean;
    return self;
}

@end

@implementation DocWin (Delegate)

- textDidGetKeys:sender isEmpty:(BOOL)flag
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (!wFlags2.docEdited)
      [super setDocEdited:YES];
    return self;
}

- windowWillClose:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (wFlags2.docEdited&&[[PrefAgent new] confirmOnClose])
      {
        int i = NXRunAlertPanel(SAVEALERTTITLE, SAVEALERTQUERY, SAVEALERTDEFAULT, SAVEALERTALTERNATE, SAVEALERTOTHER, (strrchr(filename,'/')+1));

        if (i==NX_ALERTOTHER)
          return nil;
        if (i==NX_ALERTDEFAULT)
          if (!named)
            return [self performSaveAs:self];
          else
            return [self performSave:self];
      }
    return self;
}

- windowWillResize:sender toSize:(NXSize *)frameSize
{
DEBUG_FUNC1(DEBUGLEVEL);
    if (frameSize->height<MINWINDOWHEIGHT)
      frameSize->height = MINWINDOWHEIGHT;
    return self;
}

@end

@implementation DocWin (Private)

- (void)_newAuxiliary
{
    NXRect r;

DEBUG_FUNC1(DEBUGLEVEL);
    [self useOptimizedDrawing:YES];
    [[scrollView docView] getFrame:&r];
    [scrollView setVertScrollerRequired:YES];
    [scrollView setHorizScrollerRequired:NO];
    [scrollView setDynamicScrolling:YES];
    textView = [[DocText allocFromZone:docZone] initFrame:&r];
    DEBUG_ASSERT(textView!=nil)
    [textView moveTo:0.0 :0.0];
    [textView notifyAncestorWhenFrameChanged:YES];
    [textView setVertResizable:YES];
    [textView setSelectable:YES];
    [textView setEditable:YES];
    if ([[PrefAgent new] reverseVideo])
      {
	[textView setBackgroundGray:NX_BLACK];
	[scrollView setBackgroundGray:NX_BLACK];
	[textView setTextGray:NX_WHITE];
      }
    [textView setAutosizing:NX_WIDTHSIZABLE];
    r.size.height = 0.0;
    [textView setMinSize:&r.size];
    r.size.height = 1.0e30;
    [textView setMaxSize:&r.size];
    [[scrollView setDocView:textView] free];
    [scrollView setLineScroll:[textView lineHeight]];
    [scrollView setPageScroll:[textView lineHeight]];
    [textView setDelegate:self];
    filename = NXCopyStringBufferFromZone(DEFAULTFILENAME, docZone);
    DEBUG_ASSERT(filename!=NULL)
    named = NO;
}

- (void)_saveAuxiliary:(NXStream *)stream filename:(char *)file
{
    NXSelPt start,end;
    char temp[1040],*temp2;

DEBUG_FUNC1(DEBUGLEVEL);
    [textView getSel:&start :&end];
    temp2 = NXCopyStringBuffer([super title]);
    sprintf(temp, SAVINGMESSAGE, file);
    [super setTitle:temp];
    NXPing();
    [textView writeText:stream];
    if (!hasBackup&&[[PrefAgent new] keepBackupCopy])
      {
        struct stat buf;
	stat(file,&buf);
        if (buf.st_nlink>1)
	  {
	    NXStream *bstream;
	    char *tempfile=NXCopyStringBuffer(file);
	    tempfile = (char *)realloc(tempfile,strlen(file)+2);
	    strcat(tempfile,BACKUPEXTENSION);
	    if (bstream = NXMapFile(file,NX_READONLY))
	      {
	        NXSaveToFile(bstream,tempfile);
	        NXCloseMemory(bstream, NX_FREEBUFFER);
              }
	    free(tempfile);
	  }
	else
	  {
	    char *tempfile=NXCopyStringBuffer(file);
	    tempfile = (char *)realloc(tempfile,strlen(file)+2);
	    strcat(tempfile,BACKUPEXTENSION);
	    rename(file,tempfile);
	    free(tempfile);
	  }
	hasBackup=YES;
      }
    if (NXSaveToFile(stream, file)<0)
      {
        NXRunAlertPanel(SAVEALERTTITLE, SAVEERRORMESSAGE, OKSTRING, NULL, NULL);
	[super setTitle:temp2];
      }
    else
      {
        [super setDocEdited:NO];
        if (!named) 
	  [self setFilename:file];
        [self setTitleAsFilename:filename];
        named = YES;
      }
    [textView setSel:start.cp :end.cp];
    NXCloseMemory(stream, NX_FREEBUFFER);
    free(temp2);
}

@end

id findOpenFile(id self, id NXApp, char * aString)
{
    id doc,winList=[NXApp windowList];
    int i;

    DEBUG_ASSERT(winList!=nil)
    for(i=[winList count]-1, doc=[winList objectAt:i]; i>=0; i--, doc=[winList objectAt:i])
      if ([doc isMemberOf:self]&&(!NXOrderStrings((unsigned char *)[doc filename], (unsigned char *)aString, YES, -1, NULL)))
        return doc;
    return nil;
}
